// Copyright (C) 2025, ZWSOFT Co., LTD.(Guangzhou)
// Licensed under the MIT License. See LICENSE in the project root for license information.

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using TestClient.Domain;

namespace TestClient.ViewModel;
public partial class ProjectionTestViewModel : PageViewModelBase
{
    [ObservableProperty]
    private string pointId = "";
    [ObservableProperty]
    private string dirEntId = "";
    [ObservableProperty]
    private string surfaceId = "";
    [ObservableProperty] private double dirX;
    [ObservableProperty] private double dirY;
    [ObservableProperty] private double dirZ;

    [ObservableProperty]
    private string pointRelativePos = "";
    [ObservableProperty]
    private string surfaceProjectPoint = "";
    [ObservableProperty]
    private string inquiredNearestPoint = "";
    [ObservableProperty]
    private string inquiredIntersections = "";

    public IAsyncRelayCommand SelectPointCommand { get; }
    public IAsyncRelayCommand SelectDirectionCommand { get; }
    public IAsyncRelayCommand SelectSurfaceCommand { get; }
    public IAsyncRelayCommand InquireProjectionCommand { get; }
    public IAsyncRelayCommand InquireSurfaceProjectionCommand { get; }
    public IAsyncRelayCommand InquireNearestPointCommand { get; }
    public IAsyncRelayCommand InquireRayIntersectionCommand { get; }

    public ProjectionTestViewModel(
        VFAdapterWrapper wrapper,
        ISnackNotifyService notifyService
        ) : base(wrapper, notifyService)
    {
        SelectPointCommand = CreateAsyncRpcCommand(SelectPoint);
        SelectDirectionCommand = CreateAsyncRpcCommand(SelectDirection);
        SelectSurfaceCommand = CreateAsyncRpcCommand(SelectSurface);
        InquireProjectionCommand = CreateAsyncRpcCommand(InquireProjection);
        InquireSurfaceProjectionCommand = CreateAsyncRpcCommand(InquireSurfaceProjection);
        InquireNearestPointCommand = CreateAsyncRpcCommand(InquireNearestPoint);
        InquireRayIntersectionCommand = CreateAsyncRpcCommand(InquireRayIntersectionAsync);
    }

    private async Task SelectPoint()
    {
        EnsureConnected();
        PointId = await ViewModelHelpers.SelectEnt(Wrapper, "Point");
    }
    private async Task SelectDirection()
    {
        EnsureConnected();
        DirEntId = await ViewModelHelpers.SelectEnt(Wrapper, "Point");
        var entType = Wrapper.GetEntityType(DirEntId);
        switch (entType)
        {
            case GeomEntityType.Point:
                await CalcDirByTwoPoint();
                break;
            case GeomEntityType.Curve:
                break;
            default:
                throw new Exception("Unreachable");
        }
    }
    private async Task SelectSurface()
    {
        EnsureConnected();
        SurfaceId = await ViewModelHelpers.SelectEnt(Wrapper, "Surface");
    }
    private async Task InquireProjection()
    {
        EnsureConnected();
        var relation = await Task.Run(() =>
        {
            Vector3d dir = new() { x = DirX, y = DirY, z = DirZ };
            Vector3d coord = Wrapper.GetPointCoord(PointId);
            return Wrapper.TestPointOnSurfaceByProject(SurfaceId, coord, dir);
        });
        PointRelativePos = relation switch
        {
            0 => "Outside surface",
            1 => "Inside surface",
            2 => "Boundary",
            _ => "Unknown",
        };
    }

    private async Task InquireSurfaceProjection()
    {
        EnsureConnected();
        SurfaceProjectPoint = await Task.Run(() =>
        {
            return Wrapper.CreatePointByProjection(PointId, SurfaceId);
        });
    }

    private async Task InquireNearestPoint()
    {
        EnsureConnected();
        var point = await Task.Run(() =>
        {
            var coord = Wrapper.GetPointCoord(PointId);
            return Wrapper.GetMinimumDistancePointCoordinates(SurfaceId, coord);
        });
        InquiredNearestPoint = point.ToString();
    }

    private async Task InquireRayIntersectionAsync()
    {
        EnsureConnected();
        var points = await Task.Run(() =>
        {
            Vector3d dir = new() { x = DirX, y = DirY, z = DirZ };
            Vector3d coord = Wrapper.GetPointCoord(PointId);
            return Wrapper.GetPointDirSurfaceIntersections(SurfaceId, coord, dir);
        });

        InquiredIntersections = string.Join(", ", points.Select(p => p.ToString()));
    }

    private async Task CalcDirByTwoPoint()
    {
        await Task.Run(() =>
        {
            var coord1 = Wrapper.GetPointCoord(PointId);
            var coord2 = Wrapper.GetPointCoord(DirEntId);
            DirX = coord2.x - coord1.x;
            DirY = coord2.y - coord1.y;
            DirZ = coord2.z - coord1.z;
        });
    }
}
